package com.incar.chuxing.ble.utils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * 报文处理
 *
 * @author aigo
 */
public class PackageUtils {
    private volatile static short number = 100;

    final static int CRC_TABLE_XW[] = { /* CRC余式表 */
            0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601,
            0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01,
            0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00,
            0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801,
            0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00,
            0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400,
            0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201,
            0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001,
            0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600,
            0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00,
            0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01,
            0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800,
            0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01,
            0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401,
            0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200,
            0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001,
            0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600,
            0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00,
            0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01,
            0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800,
            0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01,
            0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401,
            0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200,
            0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000,
            0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601,
            0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01,
            0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00,
            0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801,
            0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00,
            0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400,
            0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201,
            0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,};

    /***
     * 判断是否是导航请求数据
     *
     * @param data
     * @return true:导航数据;false:非导航数据
     */
    public static boolean isNav(byte[] data) {
        ByteBuffer bytes = ByteBuffer.wrap(data);
        bytes.getShort();
        bytes.getShort();
        bytes.getShort();
        byte[] navCommand = {0x0E, 0x01};
        byte[] readNavCommand = new byte[2];
        bytes.get(readNavCommand);
        if (Arrays.equals(navCommand, readNavCommand)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断是否是握手请求数据
     *
     * @param data
     * @return 大于0:握手数据;等于0:非握手数据
     */
    public static short isHandshake(byte[] data) {
        ByteBuffer bytes = ByteBuffer.wrap(data);
        bytes.getShort();
        bytes.getShort();
        short id = bytes.getShort();
        byte[] handshakeCommand = {0x02, (byte) 0x086};
        byte[] readHandshakeCommand = new byte[2];
        bytes.get(readHandshakeCommand);
        if (Arrays.equals(handshakeCommand, readHandshakeCommand)) {
            return id;
        } else {
            return 0;
        }
    }


    /**
     * crc校验
     *
     * @param buff
     * @param start
     * @param length
     * @return
     */
    public static int GetCheckCRC_XW(byte[] buff, int start, int length) {
        int crc = 0xffff;
        for (int i = 0; i < length; i++) {
            crc = (crc >>> 8) ^ CRC_TABLE_XW[(crc ^ buff[start + i]) & 0xff];
        }
        return crc;
    }

    /**
     * 累加和算法
     *
     * @param buff
     * @param start
     * @param length
     * @return
     */
    public static byte GetCheckSUM_XW(byte[] buff, int start, int length) {
        byte sum = 0;
        for (int i = 0; i < length; i++) {
            sum += buff[i + start];
        }
        return sum;
    }

    private static short getReverseShort(byte[] b, int index, boolean flag) {
        if (flag) {// Big endian
            return (short) (((b[index + 1] << 8) | b[index] & 0xff));
        } else {
            return (short) (((b[index - 1] << 8) | b[index] & 0xff));
        }
    }


    private static byte[] shortToByteArray(short s, boolean flag) {
        byte[] shortBuf = new byte[2];
        if (flag) {
            shortBuf[0] = (byte) (s & 0xff);
            shortBuf[1] = (byte) ((s >>> 8) & 0xff);
        } else {// Big endian
            shortBuf[0] = (byte) ((s >>> 8) & 0xff);
            shortBuf[1] = (byte) (s & 0xff);
        }
        return shortBuf;
    }

    /**
     * GPS信息包
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @param gpsTime   GPS时间
     * @param speed     速度
     * @param direction 方向
     * @param altitude  海拔高度
     * @param salCount  星况
     * @return
     */
    public static byte[] gpsPackage(double longitude, double latitude,
                                    long gpsTime, float speed, float direction, double altitude,
                                    int salCount) {
        ByteBuffer bytes = ByteBuffer.allocate(19);
        bytes.putInt((int) (gpsTime / 1000)); // GPS时间
        bytes.put(gpsTransformation(longitude)); // 经度
        bytes.put(gpsTransformation(latitude)); // 维度
        bytes.putShort(new BigDecimal(speed).multiply(new BigDecimal(3.6))
                .multiply(new BigDecimal(10)).shortValue()); // 速度
        bytes.put((byte) new BigDecimal(direction).divide(new BigDecimal(2), 0,
                BigDecimal.ROUND_HALF_UP).intValue()); // 方向
        bytes.putShort(new BigDecimal(altitude).shortValue());
        bytes.put((byte) 3);
        bytes.put((byte) salCount);
        bytes.flip();
        return bytes.array();
    }

    private static byte[] head7E10(byte[] content) {
        ByteBuffer send = ByteBuffer.allocate(4 + content.length);
        byte[] start = {0x7E, 0x10};
        send.put(start);
        send.putShort((short) content.length);
        send.put(content);
        return send.array();
    }

    public static byte[] gpsTransformation(double data) {
        byte[] gps = new byte[4];
        BigDecimal big = new BigDecimal(data);
        int p0 = big.intValue();
        gps[0] = (byte) p0;

        double decimalPart = big.subtract(new BigDecimal(p0)).doubleValue();
        BigDecimal p1 = new BigDecimal(decimalPart);
        p1 = p1.multiply(new BigDecimal(60));
        gps[1] = (byte) p1.intValue();

        BigDecimal p2 = p1.subtract(new BigDecimal(p1.intValue()));
        p2 = p2.multiply(new BigDecimal(100));
        gps[2] = (byte) p2.intValue();

        BigDecimal p3 = p2.subtract(new BigDecimal(p2.intValue()));
        p3 = p3.multiply(new BigDecimal(100));
        gps[3] = (byte) p3.intValue();
        return gps;
    }

    /**
     * @Title:hexString2Bytes @Description:16进制字符串转字节数组 @param src 16进制字符串 @return
     * 字节数组 @throws
     */
    public static byte[] hexString2Bytes(String src) {
        int l = src.length() / 2;
        byte[] ret = new byte[l];
        for (int i = 0; i < l; i++) {
            ret[i] = (byte) Integer
                    .valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
        }
        return ret;
    }

    /**
     * @param head         头信息 "7E10"
     * @param serialNumber 流水号
     * @param actionNum    命令标识
     * @param content      报文内容
     * @return 返回打包好的数据
     */
    public static byte[] productPackage(String head, short serialNumber,
                                        String actionNum, byte[] content) {
        if (content != null) {
            byte[] headBytes = ConvertUtil.hexString2Bytes(head);
            byte[] serialNumberBytes = ArrayUtil.getBytes(serialNumber, true);
            byte[] actionNumBytes = ConvertUtil.hexString2Bytes(actionNum);
            byte[] allContentHeadBytes = ArrayUtil.append(serialNumberBytes,
                    actionNumBytes);
            byte[] tempContent = ArrayUtil.append(allContentHeadBytes, content);
            short crc = (short) GetCheckCRC_XW(tempContent, 0,
                    tempContent.length);
            byte[] allContentBytes = ArrayUtil.append(tempContent,
                    ArrayUtil.getBytes(crc, false));
            short len = (short) allContentBytes.length;
            byte[] lenBytes = ArrayUtil.getBytes(len, true);
            byte[] allHeadBytes = ArrayUtil.append(headBytes, lenBytes);
            byte[] allBytes = ArrayUtil.append(allHeadBytes, allContentBytes);
            return allBytes;
        } else {
            return ConvertUtil.hexString2Bytes(head);
        }

    }

    // List<byte[]>

    /**
     * 可以批量上传的数据
     *
     * @param data
     * @return 0xFF表示不支持批量上传
     */
    // public static byte getFlashTypeFromActionId(byte[] data)
    // {
    // byte result = 0x00;
    // if (data == null)
    // {
    // return result;
    // }
    // short actionId = ArrayUtil.getShort(data, false);
    //
    // switch (actionId)
    // {
    // case 0x0283:// 4.2.3 批量GPS数据上传指令
    // result = 0x01;
    // break;
    // case 0x0285:// 4.2.5 打火熄火上传数据指令 (0x85)
    // result = 0x02;
    // break;
    // case 0x0284:// 4.2.4 姿态指令(0x84)
    // result = 0x03;
    // break;
    // case 0x0150:// 4.1.15 错误log上传(0x50)
    // result = 0x04;
    // break;
    // case 0x0A08:// 4.3.5 报警信息（0x08）
    // result = 0x05;
    // break;
    // case 0x0146:// 1.1.1 设备状态定时上传（健康包）指令(0x46)
    // result = 0x06;
    // break;
    // case 0x0149:// 1.1.1.1 上行指令（0x49）
    // result = 0x07;
    // break;
    // case 0x0292:// 4.2.11 VIN码指令(0x92)
    // result = 0x08;
    // break;
    // // case 0x0202://4.2.19 设备上电通知指令(0x02)
    // // result = 0x09;
    // // break;
    // case 0x0A05:// 4.3.3.2 上行指令(0x05)
    // result = 0x0A;
    // break;
    // case 0x0A07:// 4.3.4.2 上行指令(0x07)
    // result = 0x0B;
    // break;
    // case 0x0155:// 4.1.20.2 上行指令(0x55)
    // result = 0x0C;
    // break;
    // case 0x0299:// 4.2.17.2 上行指令（0x99）
    // result = 0x0E;
    // break;
    // case 0x01B0:// 4.1.1.7 控制盒升级开始和结束0x01B0
    // result = 0x0F;
    // break;
    // case 0x0202:// 4.2.19 设备上电通知指令(0x02)
    // result = 0x10;
    // break;
    // case 0x0A0A:// 4.3.6.2 上行指令(0x0A) 0x11：OBD车辆信息采集指令异步回复(0x0A09)
    // result = 0x11;
    // break;
    // case 0x0203:// 4.2.20 设备采集参数动态上传 (0x03)
    // result = 0x12;
    // break;
    // case 0x01BC:// 4.1.23 新休眠断开指令(0xBC)
    // result = 0x13;
    // break;
    // case 0x0205:// 车辆事件上传(0x0205)
    // result = 0x14;
    // break;
    // case 0x01BF:// 车辆事件阀值参数查询(0x01BF)
    // result = 0x15;
    // break;
    // case 0x0A0C:// MAC地址查询结果 (0x0A0C)
    // result = 0x16;
    // break;
    // case 0x0A0D:// 标签状态 (0x0A0D)
    // result = 0x17;
    // break;
    // case 0x01C2:// 设备动态参数设置 (0x01C2)
    // result = 0x18;
    // break;
    // case 0x0206:// 剩余油量批量报文 (0x0206)
    // result = 0x19;
    // break;
    // case 0x0A0E:// 标签设备点熄火 (0x0A0E)
    // result = 0x1A;
    // break;
    // case 0x01C4:// 设备动态参数查询异步回复(0x01C4)
    // result = 0x1B;
    // break;
    // case 0x0A0F:// 车内空气温度监控(0x0A0F)
    // result = 0x1C;
    // break;
    // case 0x0209:// 读卡器读卡信息上传(0x0209)
    // result = 0x1D;
    // break;
    //
    // default:
    // break;
    // }
    //
    // return result;
    // }

    /**
     * 把所有的byte[]属性值拼接起来
     *
     * @param obj bean对象
     * @return 拼接好的报文内容
     */
    public static byte[] getContentReflectBean(Object obj) {

        Class objClass = (Class) obj.getClass();
        byte[] contentBytes = null;
        /*
         * 得到类中的所有属性集合
         */
        Field[] fs = objClass.getDeclaredFields();
        int len = fs.length;
        for (int i = 0; i < len; i++) {
            Field oneField = fs[i];
            oneField.setAccessible(true); // 设置些属性是可以访问的
            Object value = null;
            try {
                value = oneField.get(obj);
                if (value == null) {
                    continue;
                }
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } // 得到此属性的值

            String type = oneField.getType().toString();// 得到此属性的类型
            if (type.endsWith("[B"))// byte[]
            {
                // System.out.println(oneField.getType() + "\t");
                if (contentBytes == null) {
                    contentBytes = (byte[]) value;
                } else {
                    contentBytes = ArrayUtil.append(contentBytes,
                            (byte[]) value);
                }
                // if (value != null)
                // System.out.println(
                // "name:" + oneField.getName() + "\t value = "
                // + ByteUtil.bytes2HexString((byte[]) value));
            } else if (type.endsWith("String")) {
                System.out.println(oneField.getType() + "\t是String");
                // System.out.println(
                // "name:" + oneField.getName() + "\t value = " + value);
                // oneField.set(obj,"12") ; //给属性设值
            } else if (type.endsWith("int") || type.endsWith("Integer")) {
                System.out.println(oneField.getType() + "\t是int");
                // oneField.set(obj,12) ; //给属性设值
            } else {
                System.out.println(oneField.getType() + "\t");
            }

        }
        return contentBytes;

        /*
         * 得到类中的方法
         */
        // Method[] methods = userCla.getMethods();
        // for(int i = 0; i < methods.length; i++){
        // Method method = methods[i];
        // if(method.getName().startsWith("get")){
        // System.out.print("methodName:"+method.getName()+"\t");
        // System.out.println("value:"+method.invoke(bean));//得到get 方法的值
        // }
        // }
    }


    /**
     * 给obd内容加头打包，sum
     *
     * @param content
     * @param messageType
     * @return
     */
    public static byte[] packageHeaderAndContent(byte[] content,
                                                 byte messageType) {
        byte[] head = new byte[]{0x55, messageType};
        short contentLen = (short) content.length;
        byte[] len = ArrayUtil.getBytes(contentLen, true);
        byte[] sum = new byte[]{GetCheckSUM_XW(content, 0, contentLen)};
        // 0x55A0
        // lenth(2byte)FlashTye(1byte)[流水号(2byte)|命令标识(2byte)|content|sum(1byte)]
        return ArrayUtil.byteMerger(head, new byte[]{(byte) 0xFF}, len,
                content, sum);
    }

    /**
     * 传内容，自动crc加密
     *
     * @return
     */
    private static byte[] crcEncrypt(byte[] content, byte[] header) {
        short crc = (short) PackageUtils.GetCheckCRC_XW(content, 0,
                content.length);
        byte[] crcByte = ArrayUtil.getBytes(crc, false);
        int len = content.length + crcByte.length;
        byte[] lenByte = new byte[]{(byte) len};
        return ArrayUtil
                .byteMerger(header, lenByte, content, crcByte);
    }


}
